package cn.seqdata.util;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Objects;

import org.apache.commons.beanutils.ConstructorUtils;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.NestedNullException;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.data.domain.Persistable;
import org.springframework.format.support.DefaultFormattingConversionService;

/**
 * PO来自于JPA。VO来自于自定义Bean，VO没有getter/setter，属性用public修饰，只能是简单类型，如果有嵌套用下划线'_'分隔。如： <code>
 * <br>public int parent_id;
 * <br>public String parent_parent_name;
 * <br>public DateInterval interval;
 * </code>
 */
public final class PojoUtils {
	public static DefaultFormattingConversionService cs = new DefaultFormattingConversionService();

	private PojoUtils() {
	}

	public static <PO extends Persistable<?>> PO toEntity(final Object object, Class<PO> klass) {
		if(Objects.isNull(object))
			return null;

		try {
			PO entity = ConstructorUtils.invokeConstructor(klass, null);
			vo2po(object, entity);
			return entity;
		} catch(ReflectiveOperationException ex) {
			throw new ConversionException(ex);
		}
	}

	public static <VO, PO extends Persistable<?>> VO toObject(final PO entity, Class<VO> klass) {
		if(Objects.isNull(entity))
			return null;

		try {
			VO object = ConstructorUtils.invokeConstructor(klass, null);
			po2vo(entity, object);
			return object;
		} catch(ReflectiveOperationException ex) {
			throw new ConversionException(ex);
		}
	}

	// persistant object => value object
	public static <PO extends Persistable<?>> void po2vo(final PO entity, Object object) {
		Objects.requireNonNull(object);

		try {
			Class<?> klass = object.getClass();
			for(Field field : klass.getDeclaredFields()) {
				int mod = field.getModifiers();
				if(Modifier.isPublic(mod) && !Modifier.isStatic(mod)) {
					String fieldName = field.getName();
					String propName = fieldName.replace('_', '.');

					try {
						Object value = PropertyUtils.getNestedProperty(entity, propName);
						field.set(object, cs.convert(value, field.getType()));
					} catch(NestedNullException ex) {
						// 直接忽略，层级中间出现空值很正常
					}
				}
			}
		} catch(ReflectiveOperationException ex) {
			throw new ConversionException(ex);
		}
	}

	// value object => persistant object
	public static <PO extends Persistable<?>> void vo2po(final Object object, PO entity) {
		Objects.requireNonNull(entity);

		try {
			Class<?> klass = object.getClass();
			for(Field field : klass.getDeclaredFields()) {
				Object value = field.get(object);

				int mod = field.getModifiers();
				if(Modifier.isPublic(mod) && !Modifier.isStatic(mod)) {
					String fieldName = field.getName();

					int s = 0, e;
					Object curr = entity;
					while(-1 != (e = fieldName.indexOf('_', s))) {
						String propName = fieldName.substring(s, e);

						Object child = PropertyUtils.getProperty(curr, propName);
						// null==value，不自动创建，即使创建了也是空对象
						if(Objects.nonNull(value) && Objects.isNull(child)) {
							Class<?> childClass = PropertyUtils.getPropertyType(curr, propName);
							child = ConstructorUtils.invokeConstructor(childClass, null);
							PropertyUtils.setProperty(curr, propName, child);
						}

						curr = child;
						s = e + 1;
					}

					try {
						String propName = fieldName.substring(s);
						Class<?> childClass = PropertyUtils.getPropertyType(curr, propName);
						PropertyUtils.setProperty(curr, propName, cs.convert(value, childClass));
					} catch(NestedNullException ex) {
						// 直接忽略，层级中间出现空值很正常
					}
				}
			}
		} catch(ReflectiveOperationException ex) {
			throw new ConversionException(ex);
		}
	}
}
